// @HEADER
//*********************************************************************//
//  SiYuan: A numerical PDE solver                                     //
//  Copyright (2022) YUAN Xi                                           //
//  This Software is released under the BSD 2-Clause license detailed  //
//  in the file "LICENSE" in the top-level SiYuan directory            //
//*********************************************************************//
// @HEADER

#ifndef _BC_DIRICHLET_HPP
#define _BC_DIRICHLET_HPP

#include "Teuchos_ParameterList.hpp"

#include "Panzer_STK_Interface.hpp"
#include "Panzer_GlobalIndexer.hpp"
#include "PanzerCore_config.hpp"
#include "Panzer_EvaluatorsRegistrar.hpp"
#include "Panzer_LinearObjContainer.hpp"

#include "valfunctor.hpp"

#include <string>
#include <map>
#include <set>

namespace SiYuan {

//**********************************************************************
class SomeCondition
{
    protected:
      unsigned int m_group_id;     // group ID

    public:
      SomeCondition():m_group_id(0) {};
      SomeCondition(unsigned int& g):m_group_id(g) {};
      bool isActive() const {return true;}
};

//**********************************************************************
template< typename ScalarT >
class Dirichlet: public SomeCondition 
{
  private:
    std::string  m_nodeset_name;
    std::string  m_edgeset_name;
    std::string  m_type;
    Teuchos::Array<std::string>  m_dof_name;
    XYZLib::variable<ScalarT>    m_value;  

  public:
    Dirichlet(const Teuchos::ParameterList& params, const Teuchos::RCP<const panzer_stk::STK_Interface>&,
      const Teuchos::RCP<const panzer::GlobalIndexer> & indexer );

    ScalarT getValue()  const
    { return m_value.fetch()[0]; }
	
	  std::valarray<ScalarT> getValues() const
	  { return m_value.fetch(); }

    std::string getType() const
    { return m_type; }

    const std::string getNodeSetName() const
    { return m_nodeset_name; }

    const std::string getDofName() const
    { return m_dof_name[0]; }

  public:
    std::string  m_method;
    double m_penalty;
    //working variables
    std::vector< panzer::LocalOrdinal > m_local_dofs;
    std::vector< panzer::GlobalOrdinal > m_global_dofs;
    std::set< panzer::GlobalOrdinal > m_global_nodes;
};

template< typename ScalarT >
void readDirichletCondition( std::vector< Dirichlet<ScalarT> >& bcs, const Teuchos::ParameterList& p, 
    const Teuchos::RCP<const panzer_stk::STK_Interface>& mesh,
    const Teuchos::RCP<const panzer::GlobalIndexer> & indexer);

//**********************************************************************
template<typename EvalT, typename Traits> class DirichletsEvalutor;

template<typename Traits>
class DirichletsEvalutor<panzer::Traits::Residual, Traits>
: public PHX::EvaluatorWithBaseImpl<Traits>
{
  using ScalarT = typename panzer::Traits::Residual::ScalarT;

  public:
    DirichletsEvalutor(const std::vector< Dirichlet<ScalarT> >& p, const Teuchos::RCP<const panzer_stk::STK_Interface>& mesh);
    void preEvaluate(typename Traits::PreEvalData d);
    void evaluateFields(typename Traits::EvalData d);

  protected:
    std::map< panzer::LocalOrdinal, ScalarT > m_dirichlets;
    Teuchos::RCP<panzer::LinearObjContainer> m_GhostedContainer;
};

//**********************************************************************
template<typename Traits>
class DirichletsEvalutor<panzer::Traits::Jacobian, Traits>
: public PHX::EvaluatorWithBaseImpl<Traits>
{
  using ScalarT = typename panzer::Traits::Residual::ScalarT;

  public:
    DirichletsEvalutor(const std::vector< Dirichlet<ScalarT> >& p, const Teuchos::RCP<const panzer_stk::STK_Interface>& mesh);
    void preEvaluate(typename Traits::PreEvalData d);
    void evaluateFields(typename Traits::EvalData d);

  protected:
    std::map< panzer::LocalOrdinal, ScalarT > m_dirichlets;
    Teuchos::RCP<panzer::LinearObjContainer> m_GhostedContainer;
};

//**********************************************************************
template<typename Traits>
class DirichletsEvalutor<panzer::Traits::Tangent, Traits>
: public PHX::EvaluatorWithBaseImpl<Traits>
{
  using ScalarT = typename panzer::Traits::Residual::ScalarT;

  public:
    DirichletsEvalutor(const std::vector< Dirichlet<ScalarT> >& p,  const Teuchos::RCP<const panzer_stk::STK_Interface>& mesh);
    void preEvaluate(typename Traits::PreEvalData d);
    void evaluateFields(typename Traits::EvalData d);

  protected:
    std::map< panzer::LocalOrdinal, ScalarT > m_dirichlets;
    Teuchos::RCP<panzer::LinearObjContainer> m_GhostedContainer;
};


}

#include "Dirichlet_impl.hpp"

#endif
